import 'reflect-metadata';
import { Test } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import request from 'supertest';
import { UsersModule } from '../src/users/users.module';
import { AuthModule } from '../src/auth/auth.module';
import { User } from '../src/users/user.entity';
import { HttpExceptionFilter } from '../src/shared/http-exception.filter';
import { LoggingInterceptor } from '../src/shared/logging.interceptor';

/**
 * 测试应用的端到端（E2E）功能
 */
describe('App E2E', () => {
  let app: INestApplication;
  let server: any;

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot({ isGlobal: true }),
        TypeOrmModule.forRoot({
          type: 'sqlite',
          database: ':memory:',
          entities: [User],
          synchronize: true,
          logging: false,
        }),
        UsersModule,
        AuthModule,
      ],
    }).compile();

    app = moduleRef.createNestApplication();
    app.setGlobalPrefix('api');
    app.useGlobalPipes(
      new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }),
    );
    app.useGlobalFilters(new HttpExceptionFilter());
    app.useGlobalInterceptors(new LoggingInterceptor());
    await app.init();
    server = app.getHttpServer();
  });

  afterAll(async () => {
    await app.close();
  });

  it('GET /api/auth/profile without token should be 401', async () => {
    await request(server).get('/api/auth/profile').expect(401);
  });

  it('POST /api/auth/login with wrong credentials should be 401', async () => {
    await request(server)
      .post('/api/auth/login')
      .send({ username: 'admin', password: 'wrong' })
      .expect(401);
  });

  it('POST /api/auth/login admin -> GET profile -> GET users (admin)', async () => {
    const loginRes = await request(server)
      .post('/api/auth/login')
      .send({ username: 'admin', password: 'admin123' })
      .expect(201);
    const token = loginRes.body.access_token;
    expect(typeof token).toBe('string');

    await request(server)
      .get('/api/auth/profile')
      .set('Authorization', `Bearer ${token}`)
      .expect(200);

    const usersRes = await request(server)
      .get('/api/users')
      .set('Authorization', `Bearer ${token}`)
      .expect(200);
    expect(Array.isArray(usersRes.body)).toBe(true);
    const admin = usersRes.body.find((u: any) => u.username === 'admin');
    expect(admin).toBeDefined();
    expect(admin.password).toBeUndefined();
  });

  it('Users CRUD: create -> get -> patch (admin) -> delete (admin)', async () => {
    const login = await request(server)
      .post('/api/auth/login')
      .send({ username: 'admin', password: 'admin123' })
      .expect(201);
    const token = login.body.access_token;

    const createdRes = await request(server)
      .post('/api/users')
      .send({ username: 'charlie', password: 'password', email: 'charlie@example.com' })
      .expect(201);
    expect(createdRes.body.username).toBe('charlie');

    const id = createdRes.body.id;
    const getRes = await request(server)
      .get(`/api/users/${id}`)
      .set('Authorization', `Bearer ${token}`)
      .expect(200);
    expect(getRes.body.username).toBe('charlie');

    const patchRes = await request(server)
      .patch(`/api/users/${id}`)
      .set('Authorization', `Bearer ${token}`)
      .send({ email: 'charlie2@example.com' })
      .expect(200);
    expect(patchRes.body.email).toBe('charlie2@example.com');

    await request(server)
      .delete(`/api/users/${id}`)
      .set('Authorization', `Bearer ${token}`)
      .expect(200);
  });

  it('admin-only routes should return 403 for non-admin', async () => {
    const loginUser = await request(server)
      .post('/api/auth/login')
      .send({ username: 'alice', password: 'password' })
      .expect(201);
    const userToken = loginUser.body.access_token;

    await request(server)
      .get('/api/users')
      .set('Authorization', `Bearer ${userToken}`)
      .expect(403);
  });
});